#include "intrins.h"

#include <STC8G.H>
#include <EEPRom.h>


sbit Led1		= P0^5;
sbit Led2		= P5^3;
sbit Upkey	= P5^2;

//xdata	unsigned int writeaddr=0xFFFF;

//xdata	unsigned char * uartrecvbuf;

//xdata	unsigned int uartrecvlength=0;

//xdata	unsigned int writebytescnt=0;
//xdata	unsigned int writestartaddr=0;

extern xdata unsigned int recvlength;
extern xdata unsigned int writelength;
extern xdata unsigned char databuff[600];
extern xdata unsigned char recvtimeout;
extern xdata unsigned char sendbuff[15];
extern xdata unsigned char sendcnt;
extern xdata unsigned char datasendlength;


extern bit RxLock;

xdata unsigned int otaPreCheckSum=0xFFFF;
xdata unsigned int writeaddr;
xdata unsigned int writeaddrcodestart;
xdata unsigned int recvaddr=0;




void delayms(unsigned int dcnt)
{
    while(dcnt > 0) dcnt -- ;
}


void delay1s(unsigned int dcnt1)
{
    while(dcnt1 > 0)
    {
    delayms(5000);
    dcnt1 -- ;
    }
}



void user_main1(void)//用户代码1
{

}
void user_main2(void)//用户代码2
{

}

void ota_main(void)//升级程序代码
{
	bit UserCodeArea2_Ready=0;
	bit whilebit=1;

	
	unsigned char* database;
	unsigned int length;
	unsigned int databaselength;
	unsigned int database_sum=0; 
	unsigned int check_sum=0; 
						
	unsigned int TotalCodeLength=0;
		WDT_CONTR |= 0x10;
		//确认代码区2是否己加载
	if(0x02==IapReadByte(0x8000)&&0x82==IapReadByte(0x8001)&&0x00==IapReadByte(0x8002))
		UserCodeArea2_Ready=1;
	
	
//						Led1=1;Led2=0;
//				delay1s(100);
//					Led1=0;Led2=1;				
//					delay1s(100);		
//						Led1=1;Led2=0;
//				delay1s(100);
//					Led1=0;Led2=1;				
//					delay1s(100);		
//						Led1=1;Led2=0;
//				delay1s(100);
//					Led1=0;Led2=1;				
//					delay1s(110);		
//						Led1=1;Led2=0;
//				delay1s(500);
//					Led1=0;Led2=1;				
//					delay1s(500);		
//						Led1=1;Led2=0;
//				delay1s(500);
//					Led1=0;Led2=1;				
//					delay1s(500);		




	
	
	
	//切换到升级串口
			EA = 0;
			RI=0;
//			P_SW1 = 0x00;                               //RXD/P3.0, TXD/P3.1
		P_SW1 = 0x40;                               //RXD_2/P3.6, TXD_2/P3.7
//		P_SW1 = 0x80;                               //RXD_3/P1.6, TXD_3/P1.7
//		P_SW1 = 0xc0;                               //RXD_4/P4.3, TXD_4/P4.4



	//擦自定义中断向量区
	IapEraseSector(0x0200);
	//uart中断指向CODE:0600H
	IapProgramByte(0x223,0x02);IapProgramByte(0x224,0x06);IapProgramByte(0x225,0x00); 
	
	
	
	RI=0; TI=0;
	ES = 1;
	EA = 1;
	
	Led1=1;Led2=1;		
					
					
	while(1) //进入OTA流程
	{
			WDT_CONTR |= 0x10;
		
		//接收程序代码,通过跳转指令位置确定要更新的代码区块(目前不判断一直更新区块1)

		
		//发送请求升级指令 //01 05 70 02 55 50 49 A6 //模组启动升级任务
		SBUF=0x01;while(0==TI);TI=0;
		SBUF=0x05;while(0==TI);TI=0;
		SBUF=0x70;while(0==TI);TI=0;
		SBUF=0x02;while(0==TI);TI=0;
		SBUF=0x55;while(0==TI);TI=0;
		SBUF=0x50;while(0==TI);TI=0;
		SBUF=0x49;while(0==TI);TI=0;
		SBUF=0xA6;while(0==TI);TI=0;

		
		//等待上位机确认启动升级 //升级任务启动后确认网络连接正常后,回复确认信息
		while(1==whilebit)
		{
			
			WDT_CONTR |= 0x10;
			Led1=1;Led2=0;
			delay1s(50);
			Led1=0;Led2=1;				
			delay1s(50);
			//TI=0;RI=0;
			if(recvtimeout>0) 
			{
				recvtimeout--;
				if(0==recvtimeout)
				{
					//01 05 60 02 52 44 4F 59
					if(0x01==databuff[0]&&0x05==databuff[1]&&0x60==databuff[2]&&0x02==databuff[3]&&0x52==databuff[4]&&0x44==databuff[5]&&0x4F==databuff[6]&&0x59==databuff[7]) whilebit=0;

					recvlength=0;
					writelength=0;
				}
			}
		}
		
		
		
		//擦除程序区1		
		for(writeaddr=0x7E00;writeaddr>=0x2000;writeaddr-=0x0200) //从后住前擦除,因为验证字节在后面
		{
			IapEraseSector(writeaddr);
		}
		
		//写入预校验值otaPreCheckSum
		IapProgramByte(0x7ffa,otaPreCheckSum>>8);
		IapProgramByte(0x7ffb,otaPreCheckSum);
		
		//擦除程序区2
//		if(0==UserCodeArea2_Ready)
//		{
//			for(writeaddr=0xDE00;writeaddr>=0x8000;writeaddr-=0x0200) //从后住前擦除,因为验证字节在后面
//			{
//				IapEraseSector(writeaddr);
//			}
//		}
		
		
		//发送请求数据指令 //01 05 70 02 52 44 4B 99  //模组等待STC该命令以说明单片机己准备好接收数据,请模组下载文件并发送数据
		SBUF=0x01;while(0==TI);TI=0;
		SBUF=0x05;while(0==TI);TI=0;
		SBUF=0x70;while(0==TI);TI=0;
		SBUF=0x02;while(0==TI);TI=0;
		SBUF=0x52;while(0==TI);TI=0;
		SBUF=0x44;while(0==TI);TI=0;
		SBUF=0x4B;while(0==TI);TI=0;
		SBUF=0x99;while(0==TI);TI=0;
		
	
		
		//初始化要写入的起始地址
		recvaddr=0x0000;
		writeaddr=0x2000;
		writeaddrcodestart=0x2000;
		Led2=0;		
		
		writelength=0;
		recvlength=0;
		//写程序区1//循环写，每次写512,从前往后写入，最后写版本和校验，验证数据
		while(1)//for(;;)
		{
			//写入区块定义,只取HEX文件的0000H~01FFH和2200H~7FFFH,分别写入到2000H~21FFH和2200H~7FFFH即可
			/*
			1.0000H~01FFH地址写入到2000H~21FFH,该区域为用户代码地跳地址和中断向量地址区，该区域不能有代码出来,只能包含跳转指令
			2.0200H~03FFH地址,该区域为免冲突保留不用,(因为该区域为中断向量中转地址)
			3~16.0400H~1FFFH地址,该区域为免冲突保留不用,(因为该区域为bootload代码或IAP代码运行的地址)
			17.2200H~7FFFH地址写入到2200H~7FFFH,该区域为用户代码区域(其中最后16个字节用来标识软件版本号,校验值,校验验证值)
			
	
			*/
			
			
//			delay1s(1);	
//			if(recvtimeout>0) 
//				recvtimeout--;
//			else
//			{
//			while(recvlength>writelength)
//				{
//					SBUF=databuff[writelength];
//					writelength++;
//				}
//			}

			if(recvtimeout>0) 
			{
				recvtimeout--;
				if(0==recvtimeout)
				{
					//结束
					if('M'==databuff[0]&&'L'==databuff[1]&&'U'==databuff[2]&&'P'==databuff[3])
					{
						//正常结束
						if('O'==databuff[4]&&'V'==databuff[5])
						{
							//写入校验验证值
							IapProgramByte(0x7ffe,databuff[6]);
							IapProgramByte(0x7fff,databuff[7]);
							break;
						}							
					
					}						
					
					//地址与数据和校验
					RxLock=1;//锁数据,串口不再接收数据和更改缓存区。
					writeaddrcodestart=databuff[0]; writeaddrcodestart=writeaddrcodestart<<8; writeaddrcodestart|=databuff[1];
					databaselength=databuff[2]; databaselength=databaselength<<8; databaselength|=databuff[3]; 
					//if(databaselength>512) databaselength=512;
					database=&databuff[0];
					
					if(writeaddr==writeaddrcodestart&&recvlength==(databaselength+6))//地址顺序与数据长度比对通过
					{
						
						length=databaselength+4;
						database_sum=0;
						while(length--)
						{ 
							database_sum += *database++; 
						} 
						
						check_sum=*database++; check_sum=check_sum<<8; check_sum|=*database;
						
						if(database_sum==check_sum)//和校验通过
						{
							database=&databuff[4];
							while(databaselength>writelength)
							{
								IapProgramByte(writeaddr,*database++);
								//SBUF=databuff[writelength];
								//while(0==TI);
								writeaddr++;
								//recvaddr++;
								writelength++;
							}
							//累计代码总长度
							TotalCodeLength+=databaselength;
							//正常应答,请求下一个数据包  01 05 70 02 4E 58 42 90
							SBUF=0x01;while(0==TI);TI=0;
							SBUF=0x05;while(0==TI);TI=0;
							SBUF=0x70;while(0==TI);TI=0;
							SBUF=0x02;while(0==TI);TI=0;
							SBUF=0x4E;while(0==TI);TI=0;
							SBUF=0x58;while(0==TI);TI=0;
							SBUF=0x42;while(0==TI);TI=0;
							SBUF=0x90;while(0==TI);TI=0;
						}
						else//和校验不通过,请求重发
						{
							//请求重发数据包 01 05 70 02 52 53 0B 97
							SBUF=0x01;while(0==TI);TI=0;
							SBUF=0x05;while(0==TI);TI=0;
							SBUF=0x70;while(0==TI);TI=0;
							SBUF=0x02;while(0==TI);TI=0;
							SBUF=0x52;while(0==TI);TI=0;
							SBUF=0x53;while(0==TI);TI=0;
							SBUF=0x0B;while(0==TI);TI=0;
							SBUF=0x97;while(0==TI);TI=0;
						}
						
					}
					else//地址顺序与数据长度比对不通过(说明数据格式不正确或数据包顺序错位或者有通讯干扰),中止升级,系统复位
					{
						//01 05 70 02 42 4B 06 5D
					}
					RxLock=0;//解锁数据,允许串口接收数据更改缓存区。
			
				
					recvlength=0;
					writelength=0;
				}
			}
				Led1=0;Led2=1;
				delay1s(10);
				Led1=1;Led2=0;				
				delay1s(10);		
				WDT_CONTR |= 0x10;

//			while(recvlength>writelength)
//			{
//				if(writelength<=0x2200||writelength>=0x2200)
//				{
//				//IapProgramByte(writeaddr,databuff[writelength]);
//				SBUF=databuff[writelength];
//				//while(0==TI);
//				//writeaddr++;
//				}
//				writelength++;
//			}

			
			//等待缓冲区准备
//			for(i=0;i<512;i++)
//			{

//			}
		}
			WDT_CONTR |= 0x10;
		
		
		//校验程序区1
		length=TotalCodeLength;
		database_sum=0;
		writeaddr=0x2000;
		while(length--)
		{ 
			database_sum += IapReadByte(writeaddr++); 
		} 
		
		//写入校验结果值
		IapProgramByte(0x7ffc,database_sum>>8);
		IapProgramByte(0x7ffd,database_sum);

	
		
		//重启芯片
		Led1=1;Led2=1;
		delay1s(100);
		Led1=0;Led2=0;
		delay1s(100);
		Led1=1;Led2=1;
		delay1s(100);
		Led1=0;Led2=0;
		delay1s(100);
		Led1=1;Led2=1;
		delay1s(100);
		Led1=0;Led2=0;
		delay1s(100);
		Led1=1;Led2=1;
		delay1s(100);
		Led1=0;Led2=0;
		delay1s(100);
		IAP_CONTR|=0x60;

	}
	


}




void main(void)//处理启动,升级验证，版本切换，异常处理
{
bit IsrCheckErr=0;
unsigned int i;
unsigned int keypresscnt;
xdata	unsigned int UserApp1SoftCRC_Info=0xffff;
xdata	unsigned int UserApp1SoftCRC_Calc=0xffff;
	
xdata	unsigned int UserApp2SoftCRC_Info=0xffff;
xdata	unsigned int UserApp2SoftCRC_Calc=0xffff;
	
xdata	unsigned long UserApp1SoftVer=0xffffffff;
xdata	unsigned long UserApp2SoftVer=0xffffffff;
	

			P0M0 = 0x20;
			P0M1 = 0x00;
			P3M0 = 0x00;
			P3M1 = 0x30;
			P5M0 = 0x08;
			P5M1 = 0x00;
	
	//UserApp1SoftCRC_Info=0;
	UserApp1SoftCRC_Info=IapReadByte(0x7ffc);
	UserApp1SoftCRC_Info=UserApp1SoftCRC_Info<<8;
	UserApp1SoftCRC_Info|=IapReadByte(0x7ffd);

	
	
//	UserApp2SoftCRC_Info=0;
//	UserApp2SoftCRC_Info=IapReadByte(0x7ffe);
//	UserApp2SoftCRC_Info=UserApp2SoftCRC_Info<<8;
//	UserApp2SoftCRC_Info=IapReadByte(0x7fff);
//	UserApp2SoftCRC_Info=UserApp2SoftCRC_Info<<8;
	
	

	
	WDT_CONTR |= 0x10;
/*************** 检查用户代码区1	*****************/
	
	//检查用户代码块1是否己经过校验
	if(0xFFFF!=UserApp1SoftCRC_Info)
	{
		
		otaPreCheckSum=IapReadByte(0x7ffa);
		otaPreCheckSum=otaPreCheckSum<<8;
		otaPreCheckSum|=IapReadByte(0x7ffb);
		
		UserApp1SoftCRC_Calc=IapReadByte(0x7ffe);
		UserApp1SoftCRC_Calc=UserApp1SoftCRC_Calc<<8;
		UserApp1SoftCRC_Calc|=IapReadByte(0x7fff);
		
		if(otaPreCheckSum!=0xFFFF&&otaPreCheckSum!=UserApp1SoftCRC_Info) UserApp1SoftCRC_Calc=0xFFFF;
		if(UserApp1SoftCRC_Calc!=UserApp1SoftCRC_Info) UserApp1SoftCRC_Calc=0xFFFF;
	}
	
	//如果代码块1己经过校验，则取出代码块1的版本号
	if(0xFFFF!=UserApp1SoftCRC_Calc)
	{
		UserApp1SoftVer=IapReadByte(0x7ff0);
		UserApp1SoftVer=UserApp1SoftVer<<8;
		UserApp1SoftVer|=IapReadByte(0x7ff1);
		UserApp1SoftVer=UserApp1SoftVer<<8;
		UserApp1SoftVer|=IapReadByte(0x7ff2);
		UserApp1SoftVer=UserApp1SoftVer<<8;
		UserApp1SoftVer|=IapReadByte(0x7ff3);

	}

	WDT_CONTR |= 0x10;
///*************** 检查用户代码区2 *****************/
//	
//		//检查用户代码块2是否己经过校验
//	if(0xFFFF!=UserApp1SoftCRC_Info)
//	{
//		UserApp2SoftCRC_Calc=0;
//		UserApp2SoftCRC_Calc=IapReadByte(0x7ffe);
//		UserApp2SoftCRC_Calc=UserApp2SoftCRC_Calc<<8;
//		UserApp2SoftCRC_Calc=IapReadByte(0x7fff);
//		UserApp2SoftCRC_Calc=UserApp2SoftCRC_Calc<<8;	
//		if(UserApp2SoftCRC_Calc!=UserApp2SoftCRC_Info) UserApp2SoftCRC_Calc=0xFFFF;
//	}
//	
//	//如果代码块2己经过校验，则取出代码块2的版本号
//	if(0xFFFF!=UserApp2SoftCRC_Calc)
//	{
//	UserApp2SoftVer=IapReadByte(0xdff0);
//	UserApp2SoftVer=UserApp2SoftVer<<8;
//	UserApp2SoftVer=IapReadByte(0xdff1);
//	UserApp2SoftVer=UserApp2SoftVer<<8;
//	UserApp2SoftVer=IapReadByte(0xdff2);
//	UserApp2SoftVer=UserApp2SoftVer<<8;
//	UserApp2SoftVer=IapReadByte(0xdff3);
//	UserApp2SoftVer=UserApp2SoftVer<<8;
//	}
//	
	
	
	WDT_CONTR |= 0x10;	
	//调试信息输出
	
		EA = 0;
			P_SW1 = 0x00;                               //RXD/P3.0, TXD/P3.1
//		P_SW1 = 0x40;                               //RXD_2/P3.6, TXD_2/P3.7
//		P_SW1 = 0x80;                               //RXD_3/P1.6, TXD_3/P1.7
//		P_SW1 = 0xc0;                               //RXD_4/P4.3, TXD_4/P4.4

		SCON = 0x50;		//8位数据,可变波特率
		AUXR |= 0x01;		//串口1选择定时器2为波特率发生器

		AUXR |= 0x04;		//定时器2时钟为Fosc,即1T
		T2L = 0xA0;		//设定定时初值
		T2H = 0xFC;		//设定定时初值
		AUXR |= 0x10;		//启动定时器2
		
		//这里需要关闭所有未知是否开启的中断
		ES = 0;
		//这里需要关闭所有未知是否开启的中断
	
		EA = 0;
	

	
	ES = 1;
	EA = 0;
	
	WDT_CONTR |= 0x10;
	writeaddr=0x7e00;//0x2000;
	for(i=0;i<512;i++)
	{
		SBUF=IapReadByte(writeaddr++);
		while(0==TI);TI=0;
	}
	
	
	WDT_CONTR |= 0x10;
	
	
	
			//远端触发要求进入升级模式
			if('M'==IapReadByte(0xFE00)&&'L'==IapReadByte(0xFE01)&&'U'==IapReadByte(0xFE02)&&'P'==IapReadByte(0xFE03))
			{
				otaPreCheckSum=IapReadByte(0xFE04);
				otaPreCheckSum=otaPreCheckSum<<8;
				otaPreCheckSum|=IapReadByte(0xFE05);
				IapEraseSector(0xFE00);

				ota_main();
			}


	WDT_CONTR |= 0x10;		

			
			/**************检查用户区代码，确认要运行的区域，更新中断向量******************/
			//如果代码区1版本号不等于0，并且大于代码区2版本号，则准备运行代码区1
			if(0x02==IapReadByte(0x2000)&&0x22==IapReadByte(0x2001)&&0x00==IapReadByte(0x2002)&&UserApp1SoftCRC_Calc!=0xFFFF)//(UserApp1SoftVer!=0&&UserApp1SoftVer>UserApp2SoftVer)
			{
				//检查所有代码块1的中断向量
					IsrCheckErr=0;
					for(i=0x203;i<=0x2FB;i+=8)
					{
						if(IapReadByte(i+0)!=IapReadByte(i+0x1E00+0)||IapReadByte(i+1)!=IapReadByte(i+0x1E00+1)||IapReadByte(i+2)!=IapReadByte(i+0x1E00+2))
						{
							IsrCheckErr=1; break;
						}
					}
					
					//恢复所有代码块1的中断向量
					if(0!=IsrCheckErr)
					{
							//擦自定义中断向量区
							IapEraseSector(0x0200);
							//将代码区中断跳转地址写入boot中转中断向量					
							for(i=0x203;i<=0x2FB;i+=8)
							{
								IapProgramByte(i+0,IapReadByte(i+0x1E00+0));IapProgramByte(i+1,IapReadByte(i+0x1E00+1));IapProgramByte(i+2,IapReadByte(i+0x1E00+2)); 
							}
							
							//恢复后再次检查所有代码块1的中断向量
							IsrCheckErr=0;
							for(i=0x203;i<=0x2FB;i+=8)
							{
								if(IapReadByte(i+0)!=IapReadByte(i+0x1E00+0)||IapReadByte(i+1)!=IapReadByte(i+0x1E00+1)||IapReadByte(i+2)!=IapReadByte(i+0x1E00+2))
								{
									IsrCheckErr=1; break;
								}
							}

					}

					if(0==IsrCheckErr)
					{
						#pragma asm
							LJMP 2000H
						#pragma endasm	
					}

			}
			

	WDT_CONTR |= 0x10;
			//如果代码区2版本号不等于0，并且大于代码区1版本号，则准备运行代码区2
			if(0x02==IapReadByte(0x8000)&&0x82==IapReadByte(0x8001)&&0x00==IapReadByte(0x8002))//(UserApp1SoftVer!=0&&UserApp1SoftVer>UserApp2SoftVer)
			{			
				//检查所有代码块2的中断向量
					IsrCheckErr=0;
					for(i=0x203;i<=0x2FB;i+=8)
					{
						if(IapReadByte(i+0)!=IapReadByte(i+0x7E00+0)||IapReadByte(i+1)!=IapReadByte(i+0x7E00+1)||IapReadByte(i+2)!=IapReadByte(i+0x7E00+2))
						{
							IsrCheckErr=1; break;
						}
					}
					
					//恢复所有代码块2的中断向量
					if(0!=IsrCheckErr)
					{
							//擦boot中转中断向量区
							IapEraseSector(0x0200);
							//将代码区中断跳转地址写入boot中转中断向量
							
							for(i=0x203;i<=0x2FB;i+=8)
							{
								IapProgramByte(i+0,IapReadByte(i+0x7E00+0));IapProgramByte(i+1,IapReadByte(i+0x7E00+1));IapProgramByte(i+2,IapReadByte(i+0x7E00+2)); 
							}
							
							//恢复后再次检查所有代码块2的中断向量
							IsrCheckErr=0;
							for(i=0x203;i<=0x2FB;i+=8)
							{
								if(IapReadByte(i+0)!=IapReadByte(i+0x7E00+0)||IapReadByte(i+1)!=IapReadByte(i+0x7E00+1)||IapReadByte(i+2)!=IapReadByte(i+0x7E00+2))
								{
									IsrCheckErr=1; break;
								}
							}

					}

					if(0==IsrCheckErr)
					{
						#pragma asm
							LJMP 8000H
						#pragma endasm
					}
				}
					
					
					

			
			/********************************/
			//程序不应该运行到这里
			
			P0M0 = 0x20;
			P0M1 = 0x00;
			P3M0 = 0x00;
			P3M1 = 0x30;
			P5M0 = 0x08;
			P5M1 = 0x00;
				Upkey=1;
			keypresscnt=0;
			while(1)
			{
					Led1=1;Led2=0;	WDT_CONTR |= 0x10;
					delay1s(100);
					Led1=0;Led2=1;	WDT_CONTR |= 0x10;		
					delay1s(100);
				
					if(0==Upkey)
					{
						keypresscnt++;
						if(keypresscnt>20) ota_main();
					}
					else
					{
						keypresscnt=0;
					}
//					SBUF=keypresscnt;
//					while(0==TI);TI=0;
					WDT_CONTR |= 0x10;
				
			}

}

